<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <title>Techniques</title>
    <style>
            pre{
                display: block;
                width:100%;
                background: rgb(225, 193, 189);
            }
            h1,h2,h3,h4,h5,h6{
                text-transform: capitalize
            }
        </style>
</head>
<body class="container">
    <h1>Chrome assignment of values</h1>
    <pre>
            let value = 3
                undefined
            value
                3
            console.log(value)
                3
                undefined
    </pre>
    <p>在运行chrome developer tool的时候， 当我们输入1，3，5行的代码的时候会出现其他结果，为什么会出现undefined，
        因为在chrome的环境之后，默认操作是执行这条statement，然后再return value，所以第一条执行完赋值，但是没有返回值就会返回undefined，
        第二条执行并没有效果，但是返回值就返回了3，第三条执行的时候会打印出3，但是返回值没有所以是undefined
    </p>
    <h1>关于width，padding和margin的问题</h1>
    <p>width的大小和margin没有关系</p>
    <p>当box-sizing的是content-box：时</p>
    <p>padding和border不被包含在定义的width和height之内。对象的实际宽度等于设置的width值和border、padding之和，
            即 ( Element width = width + border + padding )</p>
    <p>当box-sizing的是border-box：时</p>
    <p>padding和border被包含在定义的width和height之内。对象的实际宽度就等于设置的width值，即使定义有border和padding也
        不会改变对象的实际宽度，即 ( Element width = width )</p>
    <h1>实现闭包</h1>
    <pre>
function outside(x) {
    function inside(y) {
        return x + y;
    }
    return inside;
    }
    fn_inside = outside(3); // Think of it like: give me a function that adds 3 to whatever you give
                            // it
    result = fn_inside(5); // returns 8
    
    result1 = outside(3)(5); // returns 8
    </pre>
    <p>注意当函数inside返回时，x被保留了.因为外部函数内的变量存在周期比外部函数执行周期长,仔细体会</p>
    <p>虽然outside函数已经执行结束，但是里边的变量x还在被inner函数所引用</p>
    <pre>
var createPet = function(name) {
    var sex;
    
    return {
        setName: function(newName) {
        name = newName;
        },
        
        getName: function() {
        return name;
        },
        
        getSex: function() {
        return sex;
        },
        
        setSex: function(newSex) {
        if(typeof newSex === 'string' && (newSex.toLowerCase() === 'male' || 
            newSex.toLowerCase() === 'female')) {
            sex = newSex;
        }
        }
    }
    }
    
    var pet = createPet('Vivie');
    pet.getName();                  // Vivie
    
    pet.setName('Oliver');
    pet.setSex('male');
    pet.getSex();                   // male
    pet.getName();                  // Oliver
    </pre>
    <p>非常经典的一个使用function创建object的实例</p>
    <p>保留了外部函数的name和sex变量，通过返回一个由内部函数作为值组成的对象可以对保留的变量进行访问</p>
    <h1>识别declaration, assignment 和initialization的区别</h1>
   <ul>
       <li>Declaration is not to declare "value" to a variable; it's to declare the type of the variable.</li>
       <li>Assignment is simply the storing of a value to a variable.</li>
       <li>Initialization is the assignment of a value to a variable at the time of declaration.(declaration + assignment)</li>
   </ul>
   <h1>自己实现js的继承机制</h1>
   <pre>
function Employee(name='tony',dept='it'){
    this.name = name
    this.dept = dept
    //必须使用this保证方法的指向，否则的话默认指向了windows对象
    this.run = function(){
        console.log('I love running')
    }
}

function Manager(){
    //apply方法使得Manager也可以有Employee的属性的方法
    Employee.apply(this)
    this.report = []
    
}

Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;

var tony = new Manager();
tony.run()
   </pre>
   <h1>Callback</h1>
   <p>使用一个函数做为参数传入另一个函数，这样的函数叫做回调函数，回调函数的定义出现在外部函数调用的时候，外部函数的参数列表和函数体内只用callback变量替代s</p>
   <h2>why do we need call back?</h2>
   <p>you can’t just call one function after another and hope they execute in the right order.(因为可能会有需要等待回应的的异步操作) 
       Callbacks are a way to make sure certain code doesn’t execute until other code has already finished execution.</p>
    
   <h1>Promise机制</h1>
   <h2>官方案例解析</h2>
   <a href="https://www.baidu.com/link?url=JRlqW4Qa51urq9ZSAZjhgMr3D72VPbXTV-gYBsOVZcyz3X8bg_vG21g5Ahw4f1EQRbKhZh-x3Ye6oiPHFxHLHrQLscjiflVW0vluFbxZf5ppqE01gH2uom5HaQGkZPaGq_ysvOsk4C7B2M5xIc5nhg32Tyz3r2TUAaUB-5BRxLE6LXR-eFnIZU2Rpz7r845W&wd=&eqid=aebc643d0001ecbe000000065ce5ea91">同时推荐廖雪峰博客，非常清晰</a>
   <a href="http://es6.ruanyifeng.com/#docs/promise">同时推荐廖雪峰博客，非常清晰</a>
   <pre>
new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this');
})
.catch(() => {
    console.log('Do that');
})
.then(() => {
    console.log('Do this, no matter what happened before');
});

代码解析：
(resolve, reject) => {
    console.log('Initial');

    resolve();
} 等同于一个test函数，所以相当于是new Promise(test)，这一步可以初始化一个Promise对象，并执行test函数
   </pre>
   <h1>Website security</h1>
   <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Website_security">see more information</a>
    <h1>apply, call，bind</h1>
    <a href="https://medium.com/@omergoldberg/javascript-call-apply-and-bind-e5c27301f7bb">For details</a>
    <dl>
        <dt>bind</dt>
        <dd>The bind() method creates a new function that, when called, has its this keyword set to the provided value.</dd>
        <dd>var logPokemon = pokemonName.bind(pokemon); bind方法有返回值，将方法的this指针指向了被当做参数传入的对象</dd>
        <dt>apply</dt>
        <dd>The call() method calls a function with a given this value and arguments provided individually</dd>
        <dd>pokemonName.call(pokemon,'sushi', 'algorithms');  call方法没有返回值，方法的指针指向了被当做参数传入的对象</dd>
    </dl>
    <h2>difference</h2>
    <p>The only difference between how they work is that call() expects all parameters to be passed in individually, whereas apply() expects an array of all of our parameters. Example:</p>
    <pre>
var pokemon = {
    firstname: 'Pika',
    lastname: 'Chu ',
    getPokeName: function() {
        var fullname = this.firstname + ' ' + this.lastname;
        return fullname;
    }
};

var pokemonName = function(snack, hobby) {
    console.log(this.getPokeName() + ' loves ' + snack + ' and ' + hobby);
};

pokemonName.call(pokemon,'sushi', 'algorithms'); // Pika Chu  loves sushi and algorithms
pokemonName.apply(pokemon,['sushi', 'algorithms']); // Pika Chu  loves sushi and algorithms
    </pre>
    <p>The main differences between bind() and call() is that the call() method:</p>
    <ol>
        <li>Accepts additional parameters as well</li>
        <li>Executes the function it was called upon right away.</li>
        <li>The call() method does not make a copy of the function it is being called on.</li>
    </ol>
    <pre>
function add(x,y){
    return this.x+ this.y;
}

var obj = {
    x:18,
    y:20
}
//bind方法有返回值，只是先确认this指向，可以之后调用
var result = add.bind(obj)
console.log(result())
//call方法会立马执行
console.log(add.call(obj))
    </pre>
    <h1>new 操作符</h1>
    <p>构造函数和普通函数没有什么区别，只是为了区分所以将函数名的开头字母大写</p>
    <ol>
        使用new操作符会经历以下几步:
        <li>创建一个新对象</li>
        <li>将构造函数的作用域赋给了新对象(所以this就指向了这个新对象，当该函数被当做普通函数调用时，调用时this指针会默认指向window对象)</li>
        <li>执行构造函数内的代码(给对象添加属性)</li>
        <li>返回一个对象(被当做普通函数时，只会看方法体有没有return，没有默认返回对象)</li>
    </ol>
    <h1>Inheritance</h1>
    <p>注意只有函数才有prototype属性，对于实例化的对象来说他们没有prototype属性，每个实例中存在一个[[prototype]]指针指向原型对象，
        在chrome等浏览器中，我们可以通过一个隐藏的__proto__指针查看原型对象</p>
    <h1>this指针</h1>
    <ol>
        了解4项原则
        <li>default binding 默认绑定 <br>
            想象成一个孤立全局函数的调用，当其他三个原则都不满足时，这个原则可以使用;在chorme中创建一个函数，并设置断点观察，查看call-stack，我们
            会发现这个函数是被一个简单无修饰的方法引用，其他三个原则不使用。在strict mode下，this 指向的是undefined而不是window
        </li>
        <li>implicit binding <br>
            给一个函数内的第一句话加断点，查看这个函数有没有一个context object，也没看做是一个owning object或者containing object <br>
            Only the top/last level of an object property reference chain matters to the call-site. For instance: <br>
            只有引用链上的最高级或者最后一级才对call-site有影响 <br>
            <pre>
function foo() {
    console.log( this.a );
}

var obj2 = {
    a: 42,
    foo: foo
};

var obj1 = {
    a: 2,
    obj2: obj2
};

obj1.obj2.foo(); // 42
在这边最后一级是obj2而不是obj1，所以打印出来的数字是obj2
            </pre>
            <b>implicit miss</b>
            <p>容易出现的一个问题是，implicit binding会丢失自己的this指针</p>
            <pre>
function foo() {
    console.log( this.a );
}

var obj = {
    a: 2,
    foo: foo
};

var bar = obj.foo; // function reference/alias!

var a = "oops, global"; // `a` also property on global object

bar(); // "oops, global"
obj.foo() // 2
使用chrome查看call back， 第一个调用foo只有global scope，第二次调用会有local scope
这里实际上bar()和obj.foo()是不一样的，赋值的时候，可以理解为只是将右边值得引用赋值给了左边, 所以相当于 var bar = function foo(){},只是foo起了一个别名
            </pre>
            <p>另一种情况是传递一个回调函数，容易发生this指针丢失</p>
            <pre>
function foo() {
    console.log( this.a );
}

function doFoo(fn) {
    // `fn` is just another reference to `foo`

    fn(); // <-- call-site!
}

var obj = {
    a: 2,
    foo: foo
};

var a = "oops, global"; // `a` also property on global object

doFoo( obj.foo ); // "oops, global"
这里打印出来的不是obj的a 而是global的a， 本质上其实和上边的例子一样，都是用了implicit assignment以为有了context，但是本质上是起了一个别名
对于语言自带的方法也是一样的，回调在本质上就是把函数隐式赋值给了一个变量，之后再函数体最后调用，同样会造成丢失
function foo() {
	console.log( this.a );
}

var obj = {
	a: 2,
	foo: foo
};

var a = "oops, global"; // `a` also property on global object

setTimeout( obj.foo, 100 ); // "oops, global"

function setTimeout(fn,delay) {
	// wait (somehow) for `delay` milliseconds
	fn(); // <-- call-site!
}            
</pre>
        </li>
        <li>explicit binding会丢失自己的this指针
            <p>在js中，所有的函数都可以通过call和apply进行this指针的强行绑定,同时有一种explicit binding使用的模式叫做hard binding可以用来解决this指针混乱的问题</p>
            <div class="row">
                <div class="col">
                    <p>The most typical way to wrap a function with a hard binding creates a pass-thru of any arguments passed and any return value received:</p>
                    <pre>
function foo(something) {
    console.log( this.a, something );
    return this.a + something;
}

var obj = {
    a: 2
};

var bar = function() {
    return foo.apply( obj, arguments );
};

var b = bar( 3 ); // 2 3    
console.log( b ); // 5
                    </pre>
                </div>
                <div class="col">
                    <p>Another way to express this pattern is to create a re-usable helper, 实际上是一种过去方式的进化:</p>
                    <pre>
function foo(something) {
    console.log( this.a, something );
    return this.a + something;
}

// simple `bind` helper, 因为hard binding应用广泛，
//在ES5中引入了bind就是基于此,本质上还是对于apply函数的应用
function bind(fn, obj) {
    return function() {
        return fn.apply( obj, arguments );
    };
}

var obj = {
    a: 2
};

var bar = bind( foo, obj );

var b = bar( 3 ); // 2 3
console.log( b ); // 5
                    </pre>
                </div>
            </div>

        </li>   
        <li>new binding
            <p>在使用new操作符的时候会有四个基本操作</p>
            <ol>
                <li>创建空对象</li>
                <li>设置空对象的原型对象</li>
                <li>把这个空对象设置成构造函数的this binding</li>
                <li>如果构造函数没有显示返回其他对象，那么snew操作符会自动返回新创建的对象</li>
            </ol>
        </li>
    </ol>
    <h1>npm相关的问题</h1>
    <h2>npm scripts</h2>
    <a href="https://www.baidu.com/link?url=UU3o808LA-bPrgthi6DMqJMIYW4JgogMvKNBULSiLojNNecKau6PpyvvlT28rHhQ&wd=&eqid=b6413e2e00075f1c000000065d01bbf3">for more details</a>
    <p>npm 允许在package.json文件里面，使用scripts字段定义脚本命令。npm脚本就是shell脚本</p>
    <pre>
{
    // ...
    "scripts": {
        "build": "react-scripts start"
    }
    }
    </pre>
    <p>npm run提供了一种脚本的调用方式, npm run build等同于去调用node_module/.bin下的名为react-scripts的脚本，有一个参数是start
        一般一个名为XX脚本的内部 执行命令就是 node XX， 调用脚本时候的传参会在执行node的时候再次传入， 所以在XX.js中可以对这个参数进行判断和其他处理
    </p>
    <p>another example另外一个例子</p>
    <pre>
{
    // ...
    "scripts": {
        "webpack": "webpack"
    }
    }
    </pre>
    <p>npm run webpack 等同于 node ./node_modules/webpack/bin/webpack.js  (其实就是node_modules/.bin下的名为webpack的脚本文件的内容)</p>
    <p>查看当前项目的所有 npm 脚本命令，可以使用不带任何参数的npm run命令。</p>
    <h2>webpack和webpack-dev-server</h2>
    <a href="https://www.cnblogs.com/furoro/p/5780558.html">for details</a>
    <p>webpack只是构建，并不能自动更新，webpack-dev-server相当于是webpack+服务器，提供了http服务，能够监督原始文件的修改，实现内容的自动更新</p>
    <p>并且这个web服务和live server，apache或者是python创建的server都不一样，只有webpackserver创建的web服务能够实时监测内容更新</p>
    <h2>npm install save</h2>
    <a href="https://www.jianshu.com/p/560265997f1b">for more details</a>
    <p>需要注意的是，现在加不加--save都会修改packag.json</p>
    <h2>npm install globally or locally</h2>
    <p>全局安装的好处是已经在环境变量中添加了路径可以直接使用</p>
    <p>但是为了全局不被污染，我尽量选择本地安装，调用的话可以使用 npm run XX, 可以查看之前的npm脚本，举例如果是express，那我就在package.json中
        添加 scripts{"express":"express"}，所以npm run express的效果就和全局安装之后再使用 express的效果是一样的了，npm run express调用的是
    你本地安装的express包，而全局安装的express是调用的C盘的默认路径的安装包</p>
    <p>注意npx语法，使用npx webpack = npm run webpack 前提是已经在package.json文件中定义好了scripts</p>
    <h1>import 和 require</h1>
    <p>import 是es6的语法，而require是 commonJs的语法，本质上来说import/export是基于require/module export实现的</p>
    <h1>webpack</h1>
    <p>webpack 视所有文件都为模块, css , 图片, 字体, 数据等静态资源都会打包进 js 文件, 所以会需要用到 loader 文件</p>
    <p>npx webpack命令默认会去src目录下找index.js文件作为起始点，输出位置是dist中的main.js</p>
    <p>可以通过npx webpack --config webpack.config.js 来改变配置文件的路径和名字</p>
    <h2>Asset management</h2>
    <ol>
        <li>
            <caption>loading CSS</caption>
            <p>语法: 在起始的index.js中添加    import './style.css' </p>
            <p> In order to import a CSS file from within a JavaScript module, you need to install and add the style-loader and css-loader to your module configuration:</p>
            <p>为了在一个js模块中可以引用css文件，我们需要添加css-loader和style-loader</p>
            <p>他们做两件不同的事情，css-loader 会遍历 CSS 文件，然后找到 url() 表达式然后处理他们，style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中</p>
        </li>
        <li>
            <caption>loading images</caption>
            <p>语法: import Icon from './icon.png';</p>
        </li>
    </ol>
    <h2>output management</h2>
    <p>add bundle into your html</p>
    <p>可以设置不同的entry point，每个entry point有一个name 即key值，可以在output的时候使用[name]进行bundle文件的命名, filename: "[name].bundle.js"</p>
    <p>bundle的名字可能会随着我们entry point里输入的变化而改变，但是index.html引用的时候，依然是原名字，如何让index能够正确的找到原文件？可以使用HtmlWebpackPlugin</p>
    <h2>development</h2>
    <p>介绍一些在development可能用到的点，首先在config的mode中填写development</p>
    <p>在开发中会出现一些错误，但是会统统指向了bundle.js，没有指向原文件，我们将使用inline-source-map去搭建source map. 在config文件中添加devtool: 'inline-source-map'，在inspect的时候就会显示错误的原文件</p>
    <p>我们需要一些development tool用于监听原文件，原文件如果有修改会将修改同步到bundle文件中</p>
    <ol>
        有三种方法
        <li>webpack's Watch Mode (npm run watch可以自动同步修改，但是需要刷新浏览器才能查看修改)</li>
        <li>webpack-dev-server(nom run start可以满足基本需求,创建一个server默认监听8080端口,http://localhost:8080/webpack-dev-server可以查看监听的文件夹，默认加载文件夹中的index.html)</li>
        <li>webpack-dev-middleware(npm run server会将处理过的文件发送给server，已经被内置于webpack-dev-server中使用，而且被看做一个单独的包允许更多的自定义,可以和express搭配使用)</li>
    </ol>
    <a href="https://www.baidu.com/link?url=Mq6r2IKHoTncteHoY9O5VFqJxFJSdmNokNO--Fgwaw_vUYQ4ce1HEYD0Fv0ztvjhfUQEoIgLdybSMNJi90B4jZHMIl8XSAdvLf4lCevhbIK&wd=&eqid=a119d32a000101cf000000065d08d456">关于publicPath和contentBase的区分</a>
    <h2>HOT module replacement</h2>
    <p>简称HMR，主要用于开发过程，允许所有模块的自动更新，而不需要整体更新</p>
    <p>如果不使用HMR只是启动了webpack-dev-server，每次有改变都会重新全部刷新，但是有了HMR只需要刷新所所需内容，类似于Ajax</p>
    

    
    <a href="https://timonweb.com/posts/how-to-enable-es6-imports-in-nodejs/">a great example for explanation</a>
    <a href="https://www.tutorialsteacher.com/nodejs/nodejs-module-exports">module.exports的用法</a>
    <a href="https://www.baidu.com/link?url=t-0eA8AGGRWQ_MQpHfqZxBb5459EheTESNPU_rRKy2bMc9aRStHyS9V2Lw_fgy7J&wd=&eqid=8e4c9ac70000303c000000065d06ff47">require语句到底发生了什么</a>
    <h1>webpack</h1>
    <p>webpack 视所有文件都为模块, css , 图片, 字体, 数据等静态资源都会打包进 js 文件, 所以会需要用到 loader 文件</p>
    <p>npx webpack命令默认会去src目录下找index.js文件作为起始点，输出位置是dist中的main.js</p>
    <p>可以通过npx webpack --config webpack.config.js 来改变配置文件的路径和名字</p>
    <h2>Asset management</h2>
    <ol>
        <li>
            <caption>loading CSS</caption>
            <p>语法: 在起始的index.js中添加    import './style.css' </p>
            <p> In order to import a CSS file from within a JavaScript module, you need to install and add the style-loader and css-loader to your module configuration:</p>
            <p>为了在一个js模块中可以引用css文件，我们需要添加css-loader和style-loader</p>
            <p>他们做两件不同的事情，css-loader 会遍历 CSS 文件，然后找到 url() 表达式然后处理他们，style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中</p>
        </li>
        <li>
            <caption>loading images</caption>
            <p>语法: import Icon from './icon.png';</p>
        </li>
    </ol>
    <h2>output management</h2>
    <p>add bundle into your html</p>
    <p>可以设置不同的entry point，每个entry point有一个name 即key值，可以在output的时候使用[name]进行bundle文件的命名, filename: "[name].bundle.js"</p>
    <p>bundle的名字可能会随着我们entry point里输入的变化而改变，但是index.html引用的时候，依然是原名字，如何让index能够正确的找到原文件？可以使用HtmlWebpackPlugin</p>
    <h2>development</h2>
    <p>介绍一些在development可能用到的点，首先在config的mode中填写development</p>
    <p>在开发中会出现一些错误，但是会统统指向了bundle.js，没有指向原文件，我们将使用inline-source-map去搭建source map. 在config文件中添加devtool: 'inline-source-map'，在inspect的时候就会显示错误的原文件</p>
    <p>我们需要一些development tool用于监听原文件，原文件如果有修改会将修改同步到bundle文件中</p>
    <ol>
        有三种方法
        <li>webpack's Watch Mode (npm run watch可以自动同步修改，但是需要刷新浏览器才能查看修改)</li>
        <li>webpack-dev-server(nom run start可以满足基本需求,创建一个server默认监听8080端口,http://localhost:8080/webpack-dev-server可以查看监听的文件夹，默认加载文件夹中的index.html)</li>
        <li>webpack-dev-middleware(npm run server会将处理过的文件发送给server，已经被内置于webpack-dev-server中使用，而且被看做一个单独的包允许更多的自定义,可以和express搭配使用)</li>
    </ol>
    <a href="https://www.baidu.com/link?url=Mq6r2IKHoTncteHoY9O5VFqJxFJSdmNokNO--Fgwaw_vUYQ4ce1HEYD0Fv0ztvjhfUQEoIgLdybSMNJi90B4jZHMIl8XSAdvLf4lCevhbIK&wd=&eqid=a119d32a000101cf000000065d08d456">关于publicPath和contentBase的区分</a>
    <h2>HOT module replacement</h2>
    <p>简称HMR，主要用于开发过程，允许所有模块的自动更新，而不需要整体更新</p>
    <p>如果不使用HMR只是启动了webpack-dev-server，每次有改变都会重新全部刷新，但是有了HMR只需要刷新所所需内容，类似于Ajax</p>
    <h1>float的使用</h1>
    <p>float启用的时候，元素的display属性被默认设置为block，如果元素的position是绝对定位，或者display:none，float将会失效</p>
    <p>使用float之后元素被设置成了inline-block, </p>
    
    <h1>Z-index的使用</h1>
    <a href="https://www.cnblogs.com/dolphinX/p/3262469.html">有用的链接</a>
    <p>Z-index的作用主要是元素直接有重合时，可以通过设置z-index值决定显示顺序，值越大，越靠上</p>
    <ol>
        使用要求
        <li>父元素节点必须有position属性(除static之外)</li>
        <li>添加z-index的元素必须有position属性</li>
    </ol>
    <ol>
        Z-index不起作用的常见情况
        <li>父标签 position属性为relative；需要改为absolute</li>
        <li>当前标签没有position属性</li>
        <li>当前标签有float属性，需要去除</li>
    </ol>
    <h1>文档流和文本流</h1>
    <a href="https://www.baidu.com/link?url=GMSE0PYWmWm_DqKhhOH1oHYdegCg0b3KfC32YPDYSCOT6hyeR7DpZnbZfYsEr37DELLfJW8DH04vE7cWZ37cwq&wd=&eqid=ca3ae82400094b75000000065d12f7d4">有用的链接</a>
    <ol>
        简单来说：只有两种需要讨论的方式:float和绝对定位
        <li>float脱离文档流，但是没有脱离文本流</li>
        <li>绝对定位既脱离了文档流也脱离了文本流</li>
    </ol>
    <h1>HTML5中自定义data*的问题</h1>
    <a href="https://www.sitepoint.com/how-why-use-html5-custom-data-attributes/">查看更详细的信息</a>
    <p>举例来说，一个网页上有一组饭店信息，在html5之前，如果我们想要存储菜品信息以及饭店到客户的距离，我们会使用class属性，但是使用class会有一些问题</p>
    <ul>
        <li>class的主要问题不是为了存储信息，主要目的是帮助developer给不同的element添加style</li>
        <li>每一条多余的信息如果都要添加一个class的话，这样的话会导致解析js的数据的过程变得更加困难</li>
    </ul>
    <p>正确的HTML语法 </p>
    <p>除了data-的前缀，还必须包括 letters, numbers, hyphen (-), dot (.), colon (:) or underscore (_), 不能包括大写字母</p>
    <pre>
&lt;li data-type="veg" data-distance="2miles" data-identifier="10318"&gt;
    Salad King
    &lt;/li&gt;
    </pre>
    <p>在使用data自定义标签的时候，需要注意两件事</p>
    <ol>
        <li>存储的数据必须是string类型</li>
        <li>data attribute只能在没有合适的标签和属性的时候使用，例如不要存储class属性到data-class</li>
    </ol>
    <p>在js中获取data attribute</p>
    <span data-title="P段落">这是一个P</span>
    <script>
            const pEle = document.querySelector('span');
            //两种方式均可以，一个是传统的getAttribute，另一种是从元素绑定data对象中提取
            console.log(pEle.getAttribute('data-title'));
            console.log(pEle.dataset.title);
    </script>
    <p>在css中获取data attribute,使用attr函数 或者属性选择器</p>
    <pre>
    article::before {
        content: attr(data-parent);
        }
    article[data-columns='3'] {
        width: 400px;
        }
    </pre>
    

        <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>